// TextViewer.java

package net.sf.gogui.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

/** Dialog for displaying text.
    Allows syntax highlighting for analyze commands of type <i>hstring</i>.
*/
public final class TextViewer
    extends JDialog
{
    /** Callback for events generated by TextViewer. */
    public interface Listener
    {
        /** Callback if some text is selected.
            If text is unselected again this function will be called
            with the complete text content of the window.
        */
        void textSelected(String text);
    }

    public TextViewer(Frame owner, String title, String text,
                      boolean highlight, Listener listener)
    {
        super(owner, title);
        initialize(text, highlight, listener);
    }

    public TextViewer(Dialog owner, String title, String text,
                      boolean highlight, Listener listener)
    {
        super(owner, title);
        initialize(text, highlight, listener);
    }

    public void setText(String title, String text, boolean highlight)
    {
        setTitle(title);
        insertText(text);
        if (highlight)
            doSyntaxHighlight();
    }

    private JTextPane m_textPane;

    private Listener m_listener;

    private void insertText(String text)
    {
        Document doc = m_textPane.getDocument();
        while (text.charAt(text.length() - 1) == '\n')
            text = text.substring(0, text.length() - 1);
        try
        {
            doc.remove(0, doc.getLength());
            doc.insertString(0, text, null);
        }
        catch (BadLocationException e)
        {
            assert false;
        }
    }

    private void doSyntaxHighlight()
    {
        GuiUtil.addStyle(m_textPane, "title", null, null, true);
        GuiUtil.addStyle(m_textPane, "point", new Color(0.25f, 0.5f, 0.7f));
        GuiUtil.addStyle(m_textPane, "number", new Color(0f, 0.54f, 0f));
        GuiUtil.addStyle(m_textPane, "const", new Color(0.8f, 0f, 0f));
        GuiUtil.addStyle(m_textPane, "color", new Color(0.54f, 0f, 0.54f));
        m_textPane.setEditable(true);
        highlight("number", "\\b-?\\d+\\.?\\d*([Ee][+-]\\d+)?\\b");
        highlight("const", "\\b[A-Z_][A-Z_]+[A-Z]\\b");
        highlight("color",
                  "\\b([Bb][Ll][Aa][Cc][Kk]|[Ww][Hh][Ii][Tt][Ee])\\b");
        highlight("point", "\\b([Pp][Aa][Ss][Ss]|[A-Ta-t](1\\d|[1-9]))\\b");
        highlight("title", "^\\S+:(\\s|$)");
        m_textPane.setEditable(false);
    }

    private void highlight(String styleName, String regex)
    {
        Document doc = m_textPane.getDocument();
        Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
        try
        {
            CharSequence text = doc.getText(0, doc.getLength());
            Matcher matcher = pattern.matcher(text);
            while (matcher.find())
            {
                int start = matcher.start();
                int end = matcher.end();
                GuiUtil.setStyle(m_textPane, start, end - start, styleName);
            }
        }
        catch (BadLocationException e)
        {
            assert false;
        }
    }

    private void initialize(String text, boolean highlight, Listener listener)
    {
        m_listener = listener;
        JPanel panel = new JPanel(new BorderLayout());
        Container contentPane = getContentPane();
        contentPane.add(panel, BorderLayout.CENTER);
        m_textPane = new JTextPane();
        GuiUtil.setMonospacedFont(m_textPane);
        insertText(text);
        JScrollPane scrollPane = new JScrollPane(m_textPane);
        panel.add(scrollPane, BorderLayout.CENTER);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        KeyListener keyListener = new KeyAdapter()
            {
                public void keyReleased(KeyEvent e)
                {
                    int c = e.getKeyCode();
                    if (c == KeyEvent.VK_ESCAPE)
                        dispose();
                }
            };
        m_textPane.addKeyListener(keyListener);
        CaretListener caretListener = new CaretListener()
            {
                public void caretUpdate(CaretEvent event)
                {
                    if (m_listener == null)
                        return;
                    int start = m_textPane.getSelectionStart();
                    int end = m_textPane.getSelectionEnd();
                    Document doc = m_textPane.getDocument();
                    try
                    {
                        if (start == end)
                        {
                            String text = doc.getText(0, doc.getLength());
                            m_listener.textSelected(text);
                            return;
                        }
                        String text = doc.getText(start, end - start);
                        m_listener.textSelected(text);
                    }
                    catch (BadLocationException e)
                    {
                        assert false;
                    }
                }
            };
        m_textPane.addCaretListener(caretListener);
        if (highlight)
            doSyntaxHighlight();
        m_textPane.setCaretPosition(0);
        m_textPane.setEditable(false);
        packTextViewer();
    }

    private void packTextViewer()
    {
        pack();
        // Workaround for problems with oversized windows on some platforms
        // Also increase width by 10%, because automatic computation of
        // JTextPane/JTextArea width according to content text does not
        // work (maybe related to Sun Bug 4829215)
        Dimension size = getSize();
        size.width *= 1.1;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        int maxHeight = (int)(0.9 * screenSize.height);
        int maxWidth = (int)(0.9 * screenSize.width);
        if (size.height > maxHeight || size.width > maxWidth)
        {
            size.width = Math.min(size.width, maxWidth);
            size.height = Math.min(size.height, maxHeight);
        }
        setMinimumSize(new Dimension(128, 96));
        size.height = Math.max(size.height, 96);
        size.width = Math.max(size.width, 128);
        setPreferredSize(size);
        setSize(size);
    }
}
